#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdarg.h>

#include "imbus_internal.h"
#include "im_unicode.h"
#include "im_attributes.h"
#include "im_text.h"

/************************** Declaration of internal functions ***************/
static void     __im_text_initialize    (IMPointer       text);
static void     __im_text_finalize      (IMPointer       text);
static void     __im_text_copy          (IMPointer       dest,
                                         IMConstPointer  src);
static IMBool   __im_text_serialize     (IMConstPointer  obj,
                                         IMPointer       stream);
static IMBool   __im_text_deserialize   (IMPointer       obj,
                                         IMPointer       stream);
static void     __im_text_class_init    (IMPointer       klass);

static void     __im_text_ensure_size   (IMText         *text,
                                         IMSize          add_size);

static IMBool    __im_text_insert_utf8_printf_internal (IMText          *text,
                                                        IMSize           pos,
                                                        const IMChar    *format,
                                                        va_list          args);

/****************** Declaration of object and class structure ************/
struct _IMText
{
    IMObject object;

    IMSize       length;
    IMSize       capacity;
    IMUniChar   *data;
    IMAttrList  *attrs;
};

struct _IMTextClass
{
    IMObjectClass parent_class;
};

/************************ Internal varable ************************/
static const IMObjectClass *__im_text_parent_class = 0;

/************* Implementation of internal functions ***************/
static void
__im_text_initialize (IMPointer text)
{
    ((IMText*)text)->length   = 0;
    ((IMText*)text)->capacity = 0;
    ((IMText*)text)->data     = 0;
    ((IMText*)text)->attrs    = 0;
}

static void
__im_text_finalize (IMPointer text)
{
    IMText *tp = IM_TEXT (text);

    _im_assert (tp != 0);

    if (tp->data)
        im_free (tp->data);

    if (tp->attrs)
        im_object_unref (tp->attrs);
}

static void
__im_text_copy (IMPointer dest, IMConstPointer src)
{
    IMText       *dtp = IM_TEXT (dest);
    const IMText *stp = IM_CONST_TEXT (src);

    _im_assert (dtp && stp);

    __im_text_finalize (dest);

    dtp->length   = stp->length;
    dtp->capacity = stp->length;

    if (stp->data) {
        dtp->data = im_new(IMUniChar, stp->length);
        _im_assert (dtp->data);
        memcpy (dtp->data, stp->data, stp->length * sizeof (IMUniChar));
    } else {
        dtp->data = 0;
    }

    if (stp->attrs && stp->data)
        /* Newly created IMAttrList object should be owned by the IMText object.*/
        dtp->attrs = (IMAttrList *) im_object_ref (im_object_clone (stp->attrs));
    else
        dtp->attrs = 0;

    /* Call copy method of parent class */
    __im_text_parent_class->copy (dest, src);
}

static IMBool
__im_text_serialize (IMConstPointer obj, IMPointer stream)
{
    const IMText *tp = IM_CONST_TEXT (obj);
    IMStream     *sp = IM_STREAM (stream);
    IMSize    utf16len;
    IMBool    ret;

    _im_assert (tp && sp);

    /* Call parent serialize method */
    if (!__im_text_parent_class->serialize (obj, stream))
        return FALSE;

    utf16len = im_text_utf16_length (tp);

    /* Transfer content with UTF-16 enconding. to reduce the size. */
    if (utf16len) {
        IMUInt16 *buf = im_new (IMUInt16, utf16len + 1);
        utf16len = im_text_to_utf16_string (tp, buf, utf16len + 1);
        ret = im_stream_put_uint16_array (sp, buf, utf16len);
        im_free (buf);
    } else {
        ret = im_stream_put_uint16_array (sp, 0, 0);
    }
    if (!ret) return FALSE;

    if (tp->attrs)
        ret = im_stream_put_object (sp, tp->attrs);
    else
        ret = im_stream_put_void (sp);
    return ret;
}

static IMBool
__im_text_deserialize (IMPointer obj, IMPointer stream)
{
    IMText   *tp = IM_TEXT (obj);
    IMStream *sp = IM_STREAM (stream);
    IMUInt16 *buf;
    IMSize    len;

    _im_assert (tp && sp);

    /* Call parent deserialize method */
    if (!__im_text_parent_class->deserialize (obj, stream))
        return FALSE;

    if (im_stream_get_data_type (sp) != IM_TYPE_UINT16_ARRAY)
        return FALSE;

    im_text_clear (tp);

    len = im_stream_get_data_size (sp);

    if (len) {
        buf = im_new(IMUInt16,len+1);
        _im_assert (buf);
        if (!im_stream_get_uint16_array (sp, buf, len + 1)) {
            im_free (buf);
            return FALSE;
        }
        im_text_from_utf16_string (tp, buf, len);
        im_free (buf);
    }

    if (im_stream_get_data_type (sp) == IM_TYPE_VOID) {
        return im_stream_get_void (sp);
    } else if (im_stream_get_data_type (sp) == IM_TYPE_ATTR_LIST) {
        if (!tp->attrs) {
            /* Newly created IMAttrList object should be owned by the IMText object.*/
            tp->attrs = im_object_ref (im_attr_list_new ());
            _im_assert (tp->attrs);
        }
        return im_stream_get_object (sp, tp->attrs);
    }

    return FALSE;
}

static void
__im_text_class_init (IMPointer klass)
{
    IMObjectClass *cp = (IMObjectClass *)klass;

    cp->copy        = __im_text_copy;
    cp->serialize   = __im_text_serialize;
    cp->deserialize = __im_text_deserialize;

    __im_text_parent_class = im_object_class_get_parent (cp);
}

static void
__im_text_ensure_size (IMText *text, IMSize add_size)
{
    if (text->length + add_size > text->capacity) {
        IMSize     new_cap;
        IMUniChar *new_data;

        new_cap = text->length + add_size;

        /* The below implements an exponential growth policy */
        if (new_cap < text->capacity * 2)
            new_cap = text->capacity * 2;

        new_data = im_renew (IMUniChar, text->data, new_cap);

        _im_assert (new_data != 0);

        text->data     = new_data;
        text->capacity = new_cap;
    }
}

/************* Implementation of public functions ***************/
void
__im_type_register_text ()
{
    static IMTypeInfo im_text_type_info = 
    {
        sizeof (IMTextClass),      /**< class_size >**/
        __im_text_class_init,      /**< class_init >**/
        0,                         /**< class_finalize >**/

        sizeof (IMText),           /**< instance_size >**/
        __im_text_initialize,      /**< instance_init >**/
        __im_text_finalize,        /**< instance_finalize >**/
    };

    __im_type_register_builtin_class (IM_TYPE_OBJECT,
                                      IM_TYPE_TEXT,
                                      "Text",
                                      &im_text_type_info,
                                      TRUE);
}

IMText*
im_text_new ()
{
    IMText *t = (IMText *) im_object_new (IM_TYPE_TEXT);

    _im_assert (t != 0);

    return t;
}

IMText*
im_text_new_from_utf8_string (const IMChar *str, IMInt len)
{
    IMText *t = (IMText *) im_object_new (IM_TYPE_TEXT);

    _im_assert (t != 0);

    im_text_from_utf8_string (t, str, len);

    return t;
}

IMText*
im_text_new_from_utf8_printf (const IMChar *format, ...)
{
    IMText *t = (IMText *) im_object_new (IM_TYPE_TEXT);
    va_list ap;

    _im_assert (t != 0);

    va_start(ap, format);
    __im_text_insert_utf8_printf_internal (t, 0, format, ap);
    va_end (ap);

    return t;
}

IMText*
im_text_new_from_utf16_string (const IMUInt16 *str, IMInt len)
{
    IMText *t = (IMText *) im_object_new (IM_TYPE_TEXT);

    _im_assert (t != 0);

    im_text_from_utf16_string (t, str, len);

    return t;
}

IMText*
im_text_new_from_ucs4_string (const IMUniChar *str, IMInt len)
{
    IMText *t = (IMText *) im_object_new (IM_TYPE_TEXT);

    _im_assert (t != 0);

    im_text_from_ucs4_string (t, str, len);

    return t;
}

void
im_text_clear (IMText *text)
{
    _im_return_if_fail (IM_IS_TEXT (text));

    text->length = 0;

    if (text->attrs)
        im_attr_list_clear (text->attrs);
}

void
im_text_reserve (IMText *text, IMSize size)
{
    _im_return_if_fail (IM_IS_TEXT (text));

    if (size > text->length)
        __im_text_ensure_size (text, size - text->length);
}

IMSize
im_text_capacity (const IMText *text)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    return text->capacity;
}

IMBool
im_text_empty (const IMText *text)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), TRUE);

    return text->length == 0;
}

void
im_text_resize (IMText *text, IMSize new_size, IMUniChar fill_ch)
{
    _im_return_if_fail (IM_IS_TEXT (text));

    if (new_size < text->length) {
        if (text->attrs)
            im_attr_list_shrink (text->attrs, new_size, text->length - new_size);

        text->length = new_size;
    } else if (new_size > text->length) {
        __im_text_ensure_size (text, new_size - text->length);

        while (text->length < new_size)
            text->data [text->length ++] = fill_ch;
    }
}

IMText*
im_text_from_utf8_string (IMText *text, const IMChar *str, IMInt len)
{
    IMSize utf8len;

    _im_return_val_if_fail (IM_IS_TEXT (text), 0);
    _im_return_val_if_fail (str != 0 || len == 0, 0);

    im_text_clear (text);

    /* No content, just return */
    if (len == 0 || !*str)
        return text;

    utf8len = im_utf8_strlen (str, len);

    im_text_reserve (text, utf8len);

    text->length = im_utf8_string_to_ucs4 (text->data, utf8len, str, len);

    return text;
}

IMText*
im_text_from_utf16_string (IMText *text, const IMUInt16 *str, IMInt len)
{
    IMSize utf16len;

    _im_return_val_if_fail (IM_IS_TEXT (text), 0);
    _im_return_val_if_fail (str != 0 || len == 0, 0);

    im_text_clear (text);

    /* No content, just return */
    if (len == 0 || !*str)
        return text;

    utf16len = im_utf16_strlen (str, len);

    im_text_reserve (text, utf16len);

    text->length = im_utf16_string_to_ucs4 (text->data, utf16len, str, len);

    return text;
}

IMText*
im_text_from_ucs4_string (IMText *text, const IMUniChar *str, IMInt len)
{
    IMSize ucs4len;

    _im_return_val_if_fail (IM_IS_TEXT (text), 0);
    _im_return_val_if_fail (str != 0 || len == 0, 0);

    im_text_clear (text);

    /* No content, just return */
    if (len == 0 || !*str)
        return text;

    ucs4len = im_ucs4_strlen (str, len);

    im_text_reserve (text, ucs4len);

    memcpy (text->data, str, sizeof (IMUniChar) * ucs4len);

    text->length = ucs4len;

    return text;
}

IMSize
im_text_to_utf8_string (const IMText *text, IMChar *dest, IMSize dest_size)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);
    _im_return_val_if_fail (dest && dest_size > 0, 0);

    if (text->length == 0) {
        dest [0] = 0;
        return 0;
    }

    return im_ucs4_string_to_utf8 (dest, dest_size, text->data, text->length);
}

IMSize
im_text_to_utf16_string (const IMText *text, IMUInt16 *dest, IMSize dest_size)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);
    _im_return_val_if_fail (dest && dest_size > 0, 0);

    if (text->length == 0) {
        dest [0] = 0;
        return 0;
    }

    return im_ucs4_string_to_utf16 (dest, dest_size, text->data, text->length);
}

IMSize
im_text_to_ucs4_string (const IMText *text, IMUniChar *dest, IMSize dest_size)
{
    IMSize len;

    _im_return_val_if_fail (IM_IS_TEXT (text), 0);
    _im_return_val_if_fail (dest && dest_size > 0, 0);

    if (text->length == 0) {
        dest [0] = 0;
        return 0;
    }

    len = IM_MIN (text->length, dest_size);

    memcpy (dest, text->data, sizeof (IMUniChar) * len);

    if (len < dest_size) dest [len] = 0;

    return len;
}

IMSize
im_text_length (const IMText *text)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    return text->length;
}

IMSize
im_text_utf8_length (const IMText *text)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    if (text->length)
        return im_ucs4_string_utf8_length (text->data, text->length);

    return 0;
}

IMSize
im_text_utf16_length (const IMText *text)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    if (text->length)
        return im_ucs4_string_utf16_length (text->data, text->length);

    return 0;
}

IMUniChar
im_text_get_char (const IMText *text, IMSize idx)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);
    _im_return_val_if_fail (idx < text->length, IM_UNICHAR_INVALID);

    if (idx >= text->length)
        return IM_UNICHAR_INVALID;

    return text->data [idx];
}

void
im_text_set_char (IMText *text, IMSize idx, IMUniChar ch)
{
    _im_return_if_fail (IM_IS_TEXT (text));
    _im_return_if_fail (idx < text->length);
    _im_return_if_fail (im_unichar_validate (ch));

    if (!ch) {
        im_text_resize (text, idx, 0);
        return;
    }

    text->data [idx] = ch;
}

IMText*
im_text_get_sub_text (const IMText *text, IMSize pos, IMSize len)
{
    IMText     *new_text;
    IMAttrList *new_attrs;

    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    new_text = im_text_new ();

    _im_assert (new_text);

    if (pos < text->length && len) {
        im_text_from_ucs4_string (new_text, text->data + pos, (IMInt) IM_MIN(len, text->length - pos));

        if (text->attrs) {
            new_attrs = im_attr_list_get_sub_list (text->attrs, pos, IM_MIN(len, text->length - pos));
            im_text_set_attr_list (new_text, new_attrs);
        }
    }

    return new_text;
}

IMText*
im_text_prepend (IMText *text, const IMText *other)
{
    return im_text_insert (text, 0, other);
}

IMText*
im_text_prepend_utf8_string (IMText *text, const IMChar *str, IMInt len)
{
    return im_text_insert_utf8_string (text, 0, str, len);
}

IMText*
im_text_prepend_utf16_string (IMText *text, const IMUInt16 *str, IMInt len)
{
    return im_text_insert_utf16_string (text, 0, str, len);
}

IMText*
im_text_prepend_ucs4_string (IMText *text, const IMUniChar *str, IMInt len)
{
    return im_text_insert_ucs4_string (text, 0, str, len);
}

IMText*
im_text_prepend_unichar (IMText *text, IMUniChar ch)
{
    return im_text_insert_unichar (text, 0, ch);
}

IMText*
im_text_append (IMText *text, const IMText *other)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    return im_text_insert (text, text->length, other);
}

IMText*
im_text_append_utf8_string (IMText *text, const IMChar *str, IMInt len)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    return im_text_insert_utf8_string (text, text->length, str, len);
}

IMText*
im_text_append_utf16_string (IMText *text, const IMUInt16 *str, IMInt len)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    return im_text_insert_utf16_string (text, text->length, str, len);
}

IMText*
im_text_append_ucs4_string (IMText *text, const IMUniChar *str, IMInt len)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    return im_text_insert_ucs4_string (text, text->length, str, len);
}

IMText*
im_text_append_unichar (IMText *text, IMUniChar ch)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    return im_text_insert_unichar (text, text->length, ch);
}

IMText*
im_text_insert (IMText *text, IMSize pos, const IMText *other)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);
    _im_return_val_if_fail (IM_IS_TEXT (other), 0);
    _im_return_val_if_fail (pos <= text->length, 0);

    if (!other->length) return text;

    __im_text_ensure_size (text, other->length);

    if (pos < text->length)
        memmove (text->data + pos + other->length, text->data + pos, (text->length - pos) * sizeof (IMUniChar));

    memcpy (text->data + pos, other->data, other->length * sizeof (IMUniChar));

    text->length += other->length;

    if (other->attrs) {
        if (!text->attrs)
            /* Newly created IMAttrList object should be owned by the IMText object.*/
            text->attrs = im_object_ref (im_attr_list_new ());

        _im_assert (text->attrs != 0);

        im_attr_list_splice (text->attrs, other->attrs, pos, other->length);
    } else if (text->attrs) {
        im_attr_list_stretch (text->attrs, pos, other->length);
    }

    return text;
}

IMText*
im_text_insert_utf8_string (IMText *text, IMSize pos, const IMChar *str, IMInt len)
{
    IMSize utf8len;
    const IMChar *end;

    _im_return_val_if_fail (IM_IS_TEXT (text), 0);
    _im_return_val_if_fail (str || len == 0, 0);
    _im_return_val_if_fail (pos <= text->length, 0);

    if (len == 0 || !*str) return text;

    if (!im_utf8_string_validate (str, len, &end))
        _im_warn ("Invalid UTF-8 string: %s\n", str);

    len = end - str;

    utf8len = im_utf8_strlen (str, len);

    if (utf8len == 0) return text;

    __im_text_ensure_size (text, utf8len);

    if (pos < text->length)
        memmove (text->data + pos + utf8len, text->data + pos, (text->length - pos) * sizeof (IMUniChar));

    im_utf8_string_to_ucs4 (text->data + pos, utf8len, str, len);

    text->length += utf8len;

    if (text->attrs)
        im_attr_list_stretch (text->attrs, pos, utf8len);

    return text;
}

IMText*
im_text_insert_utf16_string (IMText *text, IMSize pos, const IMUInt16 *str, IMInt len)
{
    IMSize utf16len;
    const IMUInt16 *end;

    _im_return_val_if_fail (IM_IS_TEXT (text), 0);
    _im_return_val_if_fail (str || len == 0, 0);
    _im_return_val_if_fail (pos <= text->length, 0);

    if (len == 0 || !*str) return text;

    if (!im_utf16_string_validate (str, len, &end))
        _im_warn ("Invalid UTF-16 string.\n");

    len = end - str;

    utf16len = im_utf16_strlen (str, len);

    if (utf16len == 0) return text;

    __im_text_ensure_size (text, utf16len);

    if (pos < text->length)
        memmove (text->data + pos + utf16len, text->data + pos, (text->length - pos) * sizeof (IMUniChar));

    im_utf16_string_to_ucs4 (text->data + pos, utf16len, str, len);

    text->length += utf16len;

    if (text->attrs)
        im_attr_list_stretch (text->attrs, pos, utf16len);

    return text;
}

IMText*
im_text_insert_ucs4_string (IMText *text, IMSize pos, const IMUniChar *str, IMInt len)
{
    IMSize ucs4len;

    _im_return_val_if_fail (IM_IS_TEXT (text), 0);
    _im_return_val_if_fail (str || len == 0, 0);
    _im_return_val_if_fail (pos <= text->length, 0);

    if (len == 0 || !*str) return text;

    ucs4len = im_ucs4_strlen (str, len);

    if (ucs4len == 0) return text;

    __im_text_ensure_size (text, ucs4len);

    if (pos < text->length)
        memmove (text->data + pos + ucs4len, text->data + pos, (text->length - pos) * sizeof (IMUniChar));

    memcpy (text->data + pos, str, ucs4len);

    text->length += ucs4len;

    if (text->attrs)
        im_attr_list_stretch (text->attrs, pos, ucs4len);

    return text;
}

IMText*
im_text_insert_unichar (IMText *text, IMSize pos, IMUniChar ch)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);
    _im_return_val_if_fail (im_unichar_validate(ch) && ch != 0, 0);
    _im_return_val_if_fail (pos <= text->length, 0);

    __im_text_ensure_size (text, 1);

    if (pos < text->length)
        memmove (text->data + pos + 1, text->data + pos, (text->length - pos) * sizeof (IMUniChar));

    text->data [pos] = ch;
    ++ text->length;

    if (text->attrs)
        im_attr_list_stretch (text->attrs, pos, 1);

    return text;
}

IMText*
im_text_erase (IMText *text, IMSize pos, IMSize len)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    if (pos >= text->length || len == 0)
        return text;

    /* erase range extents to the end of text, the
     * text will be truncated */
    if (pos + len < text->length)
        memmove (text->data + pos, text->data + pos + len, (text->length - pos - len) * sizeof (IMUniChar));

    text->length -= IM_MIN(len, text->length - pos);

    if (text->attrs)
        im_attr_list_shrink (text->attrs, pos, len);

    return text;
}

void
im_text_add_attribute (IMText *text, IMSize pos, IMSize len, IMAttribute *attr)
{
    _im_return_if_fail (IM_IS_TEXT (text));
    _im_return_if_fail (IM_IS_ATTRIBUTE (attr));
    _im_return_if_fail (pos < text->length);
    _im_return_if_fail (len > 0);

    if (pos >= text->length || len == 0) {
        /* Make sure attr will be released if it's floating */
        im_object_ref (attr);
        im_object_unref (attr);
        return;
    }

    im_attribute_set_range (attr, pos, IM_MIN(pos + len, text->length));

    if (!text->attrs)
        /* Newly created IMAttrList object should be owned by the IMText object.*/
        text->attrs = im_object_ref (im_attr_list_new ());

    _im_assert (text->attrs != 0);

    im_attr_list_change (text->attrs, attr);
}

void
im_text_remove_attribute (IMText *text, IMSize pos, IMSize len, IMType attrtype)
{
    _im_return_if_fail (IM_IS_TEXT (text));

    if (text->attrs)
        im_attr_list_remove (text->attrs, pos, len, attrtype);
}

void
im_text_clear_attributes (IMText *text, IMSize pos, IMSize len)
{
    _im_return_if_fail (IM_IS_TEXT (text));

    if (text->attrs)
        im_attr_list_clear_range (text->attrs, pos, len);
}

IMAttrList*
im_text_get_attributes (const IMText *text, IMSize pos, IMSize len)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    if (text->attrs)
        return im_attr_list_get_sub_list (text->attrs, pos, len);

    return 0;
}

void
im_text_set_attr_list (IMText *text, IMAttrList *attrs)
{
    IMUInt start, end;

    _im_return_if_fail (IM_IS_TEXT (text));
    _im_return_if_fail (IM_IS_ATTR_LIST (attrs));

    if (text->attrs)
        im_object_unref (text->attrs);

    im_object_ref (attrs);
    text->attrs = attrs;

    im_attr_list_get_range (attrs, &start, &end);

    if (end > text->length)
        im_attr_list_clear_range (attrs, text->length, end - text->length);
}

IMAttrList*
im_text_get_attr_list (const IMText *text)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    return text->attrs;
}

IMAttrList*
im_text_dup_attr_list (const IMText *text)
{
    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    return text->attrs ? im_object_clone (text->attrs) : 0;
}

IMInt
im_text_compare (const IMText *text1, const IMText *text2)
{
    IMSize len;
    IMSize i;

    _im_return_val_if_fail (IM_IS_TEXT (text1), 0);
    _im_return_val_if_fail (IM_IS_TEXT (text2), 0);

    len = IM_MIN (text1->length, text2->length);

    for (i=0; i<len; ++i)
        if (text1->data [i] != text2->data [i])
            return ((IMInt)text1->data [i]) - ((IMInt)text2->data [i]);

    return ((IMInt)text1->length) - ((IMInt)text2->length);
}

IMInt
im_text_n_compare (const IMText *text1, const IMText *text2, IMSize len)
{
    IMSize i;

    _im_return_val_if_fail (IM_IS_TEXT (text1), 0);
    _im_return_val_if_fail (IM_IS_TEXT (text2), 0);

    len = IM_MIN (text1->length, len);
    len = IM_MIN (text2->length, len);

    for (i=0; i<len; ++i)
        if (text1->data [i] != text2->data [i])
            return ((IMInt)text1->data [i]) - ((IMInt)text2->data [i]);

    return 0;
}

static IMBool
__im_text_insert_utf8_printf_internal (IMText *text, IMSize pos, const IMChar *format, va_list args)
{
    IMInt n, size = 100;
    IMChar *p, *np;

    /* Guess we need no more than 100 bytes. */
    p = im_malloc (size);

    _im_assert (p != 0);

    while (p) {
        /* Try to print in the allocated space. */
        n = vsnprintf (p, size, format, args);

        /* If that worked, return the string. */
        if (n > -1 && n < size)
            break;

        /* Else try again with more space. */
        if (n > -1)    /* glibc 2.1 */
            size = n+1; /* precisely what is needed */
        else           /* glibc 2.0 */
            size *= 2;  /* twice the old size */

        np = im_realloc (p, size);

        _im_assert (np != 0);

        if (!np) im_free (p);

        p = np;
    }

    if (p) {
        im_text_insert_utf8_string (text, pos, p, n);
        im_free (p);
        return TRUE;
    } else {
        _im_assert_not_reached ("vsnprintf shouldn't fail. It maybe out of memory or you have a bogus vsnprintf.");
    }

    return FALSE;

}

IMText*
im_text_prepend_utf8_printf (IMText *text, const IMChar *format, ...)
{
    IMBool res;
    va_list ap;

    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    va_start(ap, format);
    res = __im_text_insert_utf8_printf_internal (text, 0, format, ap);
    va_end (ap);

    return res ? text : 0;
}

IMText*
im_text_append_utf8_printf (IMText *text, const IMChar *format, ...)
{
    IMBool res;
    va_list ap;

    _im_return_val_if_fail (IM_IS_TEXT (text), 0);

    va_start(ap, format);
    res = __im_text_insert_utf8_printf_internal (text, text->length, format, ap);
    va_end (ap);

    return res ? text : 0;
}

IMText*
im_text_insert_utf8_printf (IMText *text, IMSize pos, const IMChar *format, ...)
{
    IMBool res;
    va_list ap;

    _im_return_val_if_fail (IM_IS_TEXT (text), 0);
    _im_return_val_if_fail (pos <= text->length, 0);

    va_start(ap, format);
    res = __im_text_insert_utf8_printf_internal (text, pos, format, ap);
    va_end (ap);

    return res ? text : 0;
}



/*
vi:ts=4:nowrap:ai:expandtab
*/
